using NLog;

namespace graver
{
    /// <summary>
    /// 词法解析
    /// </summary>
    public class Lexer
    {
        private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
        /// <summary>
        /// 当前源码字符串
        /// </summary>
        private readonly string _text;
        /// <summary>
        /// 当前解析的位置
        /// </summary>
        private int _postion;

        /// <summary>
        /// 诊断信息
        /// </summary>
        private List<string> _diagnostics;

        public Lexer(string text)
        {
            _text = text;
            _postion = 0;
            _diagnostics = new();
        }

        public IEnumerable<string> Diagnostics => _diagnostics;

        /// <summary>
        /// 当前解析的字符
        /// </summary>
        /// <value></value>
        private char CurrentCharacter
        {
            get
            {
                if (_postion >= _text.Length)
                {
                    return '\0';
                }
                return _text[_postion];
            }
        }

        /// <summary>
        /// 移动到下一个解析位置
        /// </summary>
        private void Next()
        {
            _postion++;
        }

        /// <summary>
        /// 获取下一个 Token
        /// </summary>
        /// <returns>解析的 Token</returns>
        public SyntaxToken NextToken()
        {
            /* 文件尾 */
            if (_postion >= _text.Length)
            {
                return new SyntaxToken(SyntaxKind.EndOfFileToken, _postion, null, null);
            }

            /* 解析数字 */
            if (char.IsDigit(CurrentCharacter))
            {
                var start = _postion;
                while (char.IsDigit(CurrentCharacter))
                {
                    Next();
                }
                var digitLength = _postion - start;
                var digitText = _text.Substring(start, digitLength);
                if (int.TryParse(digitText, out var digitValue))
                {
                    return new SyntaxToken(SyntaxKind.NumberToken, start, digitText, digitValue);
                }
                else
                {
                    _diagnostics.Add($"The number {_text} isn't valied Int32");
                    return new SyntaxToken(SyntaxKind.BadToken, start, digitText, digitText);
                }
            }

            /* 解析空白字符 */
            if (char.IsWhiteSpace(CurrentCharacter))
            {
                var start = _postion;
                while (char.IsWhiteSpace(CurrentCharacter))
                {
                    Next();
                }
                var spaceLength = _postion - start;
                var spaceText = _text.Substring(start, spaceLength);
                return new SyntaxToken(SyntaxKind.SpaceToken, start, spaceText, null);
            }

            /* 解析操作符 */
            if ('+' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.PlusToken, _postion++, "+", null);
            }
            if ('-' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.MinusToken, _postion++, "-", null);
            }
            if ('*' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.StarToken, _postion++, "*", null);
            }
            if ('/' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.SlashToken, _postion++, "/", null);
            }
            if ('(' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.OpenParenthesisToken, _postion++, "(", null);
            }
            if (')' == CurrentCharacter)
            {
                return new SyntaxToken(SyntaxKind.CloseParenthesisToken, _postion++, ")", null);
            }

            _diagnostics.Add($"Erro: bad character input: '{CurrentCharacter}'");

            return new SyntaxToken(SyntaxKind.BadToken, _postion++, _text.Substring(_postion - 1, 1), null);
        }
    }
}